抱着玩的心态,玩中看了几道题目,本来也想着看看XNUCA的题目的,无奈没有报上名。NCTF划划水….

基本操作 ~Reverse Version.~


elf64位,直接扔到IDA中,F5后,代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
__int64 result; // rax

puts("Input flag:");
__isoc99_scanf("%64s", byte_601100);
dword_601064 = 0;
sub_400666(0);
if ( !strcmp(&s1, "bcec8d7dcda25d91ed3e0b720cbb6cf202b09fedbc3e017774273ef5d5581794") )
{
memset(&s1, 0, 0x80uLL);
dword_601064 = 0;
sub_4006BE(0, 0LL);
if ( !strcmp(&s1, "7d8dcdcaed592e1dcb07e02c36bcb2f0bf9e0bdcb0e13777237e25fd48515974") )
printf("TQL! TQL! flag: nctf{%s}\n", byte_601100);
else
puts("Emmmm.....");
result = 0LL;
}
else
{
puts("GG!");
result = 0LL;
}
return result;
}

做了两处strcmp,其中地址s1的字符分别在函数sub_40666(0)和sub_4006BE(0, 0LL)中进行了赋值。进入sub_40666(0)中,通过递归对s1进行赋值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
__int64 __fastcall sub_400666(signed int a1)
{
int v1; // eax
__int64 result; // rax

if ( a1 <= 63 )
{
v1 = dword_601064++;
*(&s1 + v1) = byte_601100[a1];
sub_400666((unsigned int)(2 * a1 + 1));
result = sub_400666((unsigned int)(2 * (a1 + 1)));
}
return result;
}

通过递归依次对s1[0],s1[1],s1[2]….进行赋值,其值byte_601100[a1],逆向可以得到输入的字符byte_601100[a1]的值应该依次为s1[0],s1[1],s1[2],编写解题脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
string_one = list("bcec8d7dcda25d91ed3e0b720cbb6cf202b09fedbc3e017774273ef5d5581794")
string_flag = list("bcec8d7dcda25d91ed3e0b720cbb6cf202b09fedbc3e017774273ef5d5581794")
z= [0]
def func1( a):
if (a<=63):
#print z[0],a
string_flag[a] = string_one[z[0]]//核心部分
z[0] = z[0] + 1
func1(2*a+1)
func1(2*(a+1))
print "*"*30
func1(0)
print "#"*30
flag = ""
for i in string_flag:
flag = flag + i
print flag

FLAG:nctf{bc2e3b4c2eb03258c5102bf9de77f57dddad9edb70c6c20febc01773e5d81947}

Some Boxes


这个题目看了真的是好久,刚刚看到四步操作如下,以为是地图题目

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
if ( v3 == 52 )                             // 4
{
subfunc1_400C62_Left();
goto LABEL_14;
}
if ( v3 > 52 )
{
if ( v3 == 53 ) // 5
{
subfunc2_400D5DDOWN();
goto LABEL_14;
}
if ( v3 == 87 ) // W
{
subfunc3_400B67UP();
goto LABEL_14;
}
}
else if ( v3 == 48 )
{ // 0
subfunc4_400A6CRIGHT_right();
goto LABEL_14;
}

跟进函数后发现:

1
result = (unsigned __int8)byte_6020A0[16 * dword5_6021B4_poi_col - 1 + dword80_6021B0_poi_line];

跟到byte_6020A0以为发现了新大陆,但是提取数据后长这样

1
2
3
4
5
6
7
8
9
10
11
12
  8   9  10  11  12  13  14  15   0   1   2   3   4   5   6   7          16 
24 25 18 19 28 29 30 31 16 17 18 19 28 29 22 23 32
40 41 34 35 36 37 38 47 40 41 42 43 44 45 38 39 48
56 57 50 59 60 53 54 55 56 57 50 51 52 61 62 55 64
72 73 66 75 76 69 78 79 72 73 66 95 76 77 78 71 80
88 89 82 83 92 85 86 87 88 89 82 83 84 85 86 87 96
104 105 98 99 108 109 110 111 104 97 98 99 100 101 102 103 112
120 121 114 123 124 125 126 127 120 113 114 123 124 125 118 119 128
136 129 130 139 144 141 134 143 136 137 138 139 132 141 134 135 144
152 145 146 147 148 149 150 151 152 145 146 147 148 157 150 151 160
168 161 162 163 164 173 174 175 168 161 170 171 172 173 166 167 176
184 185 186 187 188 189 190 191 176 177 178 179 180 181 182 183 192

这也不是地图啊,各种揪心中,然后函数中不是简单的++ – 各种操作,这都是什么!!!而且都是在和8做比较,地图里哪有那么多8。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 向左移动
__int64 subfunc1_400C62_Left()
{
__int64 result; // rax dword80_6021B0

result = (unsigned __int8)byte_6020A0[16 * dword5_6021B4_poi_col - 1 + dword80_6021B0_poi_line];// 先获取其左侧点,无效操作
if ( (_BYTE)result != 8 )
{
if ( dword80_6021B0_poi_line - 1 != dword5_6021D8_one || dword5_6021B4_poi_col != dword2_6021DC_one )// 先和第一个点做比较,碰不到第一个点
{
if ( dword80_6021B0_poi_line - 1 == dword8_6021D0_three && dword5_6021B4_poi_col == dword7_6021D4_three )// 碰到第三个点
{
if ( byte_6020A0[16 * dword7_6021D4_three - 1 + dword8_6021D0_three] == 8 )// 第三个点左移动碰到8
++dword80_6021B0_poi_line; // 指示点向右移动
else
sub_400A1C(&dword8_6021D0_three); // 否则向左移动第三个点
}
}
else if ( byte_6020A0[16 * dword2_6021DC_one - 1 + dword5_6021D8_one] == 8 )// 第一个点向左移动是8
{
++dword80_6021B0_poi_line; // 指示点向右移动
}
else
{
sub_400A1C(&dword5_6021D8_one); // 第一个点左边一个点不是8,且指示点左边第一个点行列均不碰第一个点
}
result = (unsigned int)(dword80_6021B0_poi_line-- - 1);
}
return result;
}

查看程序对byte_6020A0的操作发现存在写操作,跟入,发现一个init函数中但是要修复栈帧后(要在报错的上一行ALT+K),后发现原来真正的地图要运行后才能得到。

1
2
3
4
5
6
7
8
9
void *sub_400EB9()
{
signed int i; // [rsp+0h] [rbp-4h]
void *retaddr; // [rsp+Ch] [rbp+8h]

for ( i = 0; i <= 191; ++i )
byte_6020A0[i] ^= i;
return retaddr;
}

经过提取数据后得到地图,这才是真正的地图嘛。

1
2
3
4
5
6
7
8
9
10
11
12
13
0   1   2   3   4   5   6   7   8   9   10  11  12  13  14  15  
8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 16
8 8 0 0 8 8 8 8 8 8 8 8 0 0 8 8 32
8 8 0 0 0 0 0 8 0 0 0 0 0 0 8 8 48
8 8 0 8 8 0 0 0 0 0 8 8 8 0 0 8 64
8 8 0 8 8 0 8 8 0 0 8 20 0 0 0 8 80
8 8 0 0 8 0 0 0 0 0 8 8 8 8 8 8 96
8 8 0 0 8 8 8 8 0 8 8 8 8 8 8 8 112
8 8 0 8 8 8 8 8 0 8 8 0 0 0 8 8 128
8 0 0 8 20 8 0 8 0 0 0 0 8 0 8 8 144
8 0 0 0 0 0 0 0 0 8 8 8 8 0 8 8 160
8 0 0 0 0 8 8 8 0 8 0 0 0 0 8 8 176
8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 192

然后通过最后的输出函数中的两个关键等式定位两个关键点,也就是在起始位置为37和120的两个点最后的位置处要等于20才能输出FLAG,而我们通过主函数可以确定最开始的起始点是88

1
2
result = (unsigned __int8)byte_6020A0[16 * dword2_6021DC_one + dword5_6021D8_one];// res[37]的位置,起始值为37
result = (unsigned __int8)byte_6020A0[16 * dword7_6021D4_three + dword8_6021D0_three];// 120起始的位置,起始的值为120

一开始以为是该起始点带着其它的两个点,依次将他们送入最后的位置处,但是…error! error!再重新回到程序分析发现起始的标准点和其它的两个点根本不会相遇,而是推着他们走。再联想题目Some Box 。推箱子没问题了。按照题目要求,推一下箱子吧,手动玩游戏,得到最后的FLAG

1
2
输入: 444WW0W444W45555555450050W0000WWWWW5444WW00050W4W0000W0550544
NCTF{6a229b5df4b8e8109a38c13ceaebc90ec3953055a4dcaf602fa7f08a39491bca}

Our 16bit wars


16位的汇编代码,反编译不存在的。一遍看INT 21h一遍解析代码,程序流程如下

1
2
3
4
5
6
7
显示提示
接受输入
右移
左移
异或
判断
输出

程序中主要的核心部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
seg001:0059 sub_100F9       proc near               ; CODE XREF: sub_100AC+1A↑p
seg001:0059 mov bx, di
seg001:005B mov si, 0
seg001:005E
seg001:005E loc_100FE: ; CODE XREF: sub_100F9+19↓j
seg001:005E mov al, [bx+si] ; a[1+i]
seg001:0060 mov dl, al ; b[1+i]=a[1+i]
seg001:0062 mov cl, 3
seg001:0064 shr al, cl ; a[1+i]=a[1+i]>>3
seg001:0066 mov cl, 5
seg001:0068 shl dl, cl ; b[1+i]=b[1+i]<<5
seg001:006A xor al, dl ; a[1+i]=a[1+i]^b[1+i]
seg001:006C mov [bx+si], al ; a[1+i]=a[1+i]
seg001:006E inc si
seg001:006F cmp si, 23h ; '#'
seg001:0072 jnz short loc_100FE ; a[1+i]
seg001:0074 retn
seg001:0074 sub_100F9 endp

我们可以得指程序做的操作如下:

1
2
3
4
5
6
for (int i=0;i<0x23;i++){
b[1+i]=a[1+i];
a[1+i]=a[1+i]>>3;
b[1+i]=b[1+i]<<5;
a[1+i]=a[1+i]^b[1+i];
}

查看最后的比较函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
seg001:0044 sub_100E4       proc near               ; CODE XREF: sub_100AC+23↑p
seg001:0044 xor bx, bx
seg001:0046
seg001:0046 loc_100E6: ; CODE XREF: sub_100E4+B↓j
seg001:0046 mov al, [bx+si] ;si 在76h处
seg001:0048 cmp al, [bx+di] ; 输入处的字符串
seg001:004A jnz short loc_100F5
seg001:004C inc bx
seg001:004D cmp bx, dx
seg001:004F jnz short loc_100E6
seg001:0051 mov ax, 0
seg001:0054 retn
seg001:0055 ; ---------------------------------------------------------------------------
seg001:0055
seg001:0055 loc_100F5: ; CODE XREF: sub_100E4+6↑j
seg001:0055 mov ax, 1
seg001:0058 retn
seg001:0058 sub_100E4 endp

提取出做比较的字符串后,解题脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from z3 import *
res = [201, 104, 138, 200, 111, 7, 6, 15, 7, 198, 235, 134, 110, 110, 102, 173, 76, 141, 172, 235, 38, 110, 235, 204, 174, 205, 140, 134, 173, 102, 205, 142, 134, 141, 175]

flag = []
for i in range(0,35):

ans = BitVec('ans',8)
rev = Solver()
rev.add((ans>>3)^(ans<<5)==res[i])
print rev.check()
print rev.model()
flag.append(rev.model())
print flag
res_flat = ""
flag = [78,67,84,70,123,56,48,120,56,54,95,52,115,115,51,109,98,108,101,95,49,115,95,102,117,110,100,52,109,51,110,116,52,108,125]
print len(flag)
for i in flag:
res_flat = res_flat + chr(i)
print res_flat

flag: NCTF{80x86_4ss3mble_1s_fund4m3nt4l}

后门后门后门

IDA经过F5反编译后,直接进入flag函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
void __cdecl hereisyourflag()
{
char flagenc[32]; // [rsp+20h] [rbp-30h]
int i; // [rsp+4Ch] [rbp-4h]

flagenc[0] = 0;
flagenc[1] = 125;
flagenc[2] = 100;
flagenc[3] = 108;
flagenc[4] = 114;
flagenc[5] = 111;
flagenc[6] = 119;
flagenc[7] = 95;
flagenc[8] = 115;
flagenc[9] = 39;
flagenc[10] = 121;
flagenc[11] = 114;
flagenc[12] = 97;
flagenc[13] = 110;
flagenc[14] = 105;
flagenc[15] = 98;
flagenc[16] = 95;
flagenc[17] = 111;
flagenc[18] = 116;
flagenc[19] = 95;
flagenc[20] = 101;
flagenc[21] = 109;
flagenc[22] = 111;
flagenc[23] = 99;
flagenc[24] = 108;
flagenc[25] = 101;
flagenc[26] = 87;
flagenc[27] = 123;
flagenc[28] = 102;
flagenc[29] = 116;
flagenc[30] = 99;
flagenc[31] = 110;
for ( i = 0; i <= 15; ++i )
{
flagenc[i] ^= flagenc[31 - i];
flagenc[31 - i] ^= flagenc[i];
flagenc[i] ^= flagenc[31 - i];
}
printf("here is your real flag:%s\n", flagenc);
}

本机运行即可得到FLAG


感谢NCTF出题的师傅们,玩的很开心.